package pers.seven.beanconversion;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;

public class BeanConver {

    public static <T> T convert(Object obj, Class<T> cls){
        Class<?> objCls = obj.getClass();
        if (!objCls.isAnnotationPresent(BindClass.class))
            throw new RuntimeException(objCls.getName() + " is not bound Bind Class");
        BindClass bind = objCls.getAnnotation(BindClass.class);
        List<Class> classes = Arrays.asList(bind.binds());
        int clsIndex = classes.indexOf(cls);
        if (clsIndex < 0)
            throw new RuntimeException("There is no binding:" + cls.getName());
        Constructor<?> defConstructor = getDefaultConstructor(cls, cls.getConstructors());
        if (defConstructor == null)
            throw new RuntimeException("default constructor is not exist");
        try {
            Object target = defConstructor.newInstance(null);
            for (Field field : objCls.getDeclaredFields()){
                field.setAccessible(true);
                BindField bf = field.getAnnotation(BindField.class);
                if (bf == null || bf.relate().length == 0)
                    continue;
                String[] relates = bf.relate();
                String relateName;
                if (relates.length <= clsIndex) {
                    relateName = relates[relates.length - 1];
                } else {
                    relateName = relates[clsIndex];
                }
                Field f = cls.getDeclaredField(relateName);
                f.setAccessible(true);
                Object val = field.get(obj);
                f.set(target, val);
            }
            return (T) target;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    static Constructor<?> getDefaultConstructor(Class<?> clazz, final Constructor<?>[] constructors) {
        if (Modifier.isAbstract(clazz.getModifiers())) {
            return null;
        }
        Constructor<?> defaultConstructor = null;
        for (Constructor<?> constructor : constructors) {
            if (constructor.getParameterTypes().length == 0) {
                defaultConstructor = constructor;
                break;
            }
        }
        return defaultConstructor;
    }
}
